﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using System.Globalization;

namespace TestPDFParser
{
    class Program
    {
        public static String pdfFilePath = "D:\\3PiR\\Projects\\Elite\\EliteBillingChecker\\Data\\FEV2012\\PDF";
        public static String txtFilePath = "D:\\3PiR\\Projects\\Elite\\EliteBillingChecker\\Data\\FEV2012\\TXT";

        class Unidade
        {
            public string nome;
            public List<Turma> turmasPdf;
            public List<Turma> turmasTxt;
        };

        class Turma
        {
            public string path;
            public List<Boleto> boletos;
        };

        class Boleto
        {
            public string nome;
            public double preco;
            public string documento;
            public string path;
        };

        static void Main(string[] args)
        {
            List<String> unidadesPDF = Directory.EnumerateDirectories(pdfFilePath).ToList();
            List<String> unidadesTXT = Directory.EnumerateDirectories(txtFilePath).ToList();
            SortedList<String, String> pathAssociations = new SortedList<string, string>();

            List<Unidade> unidades = new List<Unidade>();
            foreach (String u in unidadesPDF)
                if (unidadesTXT.Contains(txtFilePath + "\\" + Path.GetFileName(u)))
                    unidades.Add(new Unidade() { nome = Path.GetFileName(u), turmasPdf = new List<Turma>(), turmasTxt = new List<Turma>()});
            List<String> Errors = new List<string>();

            foreach (Unidade unidade in unidades)
            {
                Console.WriteLine("Unidade " + unidade.nome + ":");
                List<String> pdfFiles = Directory.EnumerateFiles(pdfFilePath + "\\" + unidade.nome).ToList();
                List<String> txtFiles = Directory.EnumerateFiles(txtFilePath + "\\" + unidade.nome).ToList();
                List<Boleto> boletosPdf = new List<Boleto>();
                List<Boleto> boletosTxt = new List<Boleto>();

                #region Gather all data
                // PDF Data
                for (int i = 0; i < pdfFiles.Count; i++)
                {
                    // Lê info nos arquivos
                    String[] pdfInfo = ReadPdfFile(pdfFiles[i]);
                    foreach (String pdfInfoUnit in pdfInfo)
                    {
                        Boleto r = new Boleto();
                        DateTime date = DateTime.Parse(pdfInfoUnit.Split('\n')[pdfInfoUnit.Split('\n').ToList().IndexOf("Local de Pagamento:") - 1].Trim());
                        if (date.Month != 2)
                        {
                            Console.WriteLine("Conferir arquivo {1}/{0}", Path.GetFileName(pdfFiles[i]), Path.GetFileName(Path.GetDirectoryName(pdfFiles[i])));
                            break;
                        }

                        r.nome = RemoveStress(pdfInfoUnit.Split('\n')[pdfInfoUnit.Split('\n').ToList().IndexOf("Local de Pagamento:") + 1].Trim().Substring(7).Trim().ToUpper());
                        if (r.nome.Length > 40)
                            r.nome = r.nome.Substring(0, 40);
                        r.documento = pdfInfoUnit.Split('\n')[pdfInfoUnit.Split('\n').ToList().IndexOf("Nº Documento") + 1].Trim().Replace(".", String.Empty); 
                        r.preco = Double.Parse(pdfInfoUnit.Split('\n')[pdfInfoUnit.Split('\n').ToList().IndexOf("(=)Valor do Documento") + 1].Trim());
                        r.path = pdfFiles[i];
                        boletosPdf.Add(r);
                    }
                }

                // TXT Data
                for (int i = 0; i < txtFiles.Count; i++)
                {
                    // Cria nova turma
                    Turma t = new Turma() { path = txtFiles[i], boletos = new List<Boleto>() };

                    // Lê info nos arquivos
                    String[] txtInfo = ReadTxtFile(txtFiles[i]);
                    foreach (String txtInfoUnit in txtInfo)
                    {
                        Boleto r = new Boleto();
                        r.preco = Double.Parse(txtInfoUnit.TrimStart().Substring(127, 12)) / 100;
                        r.nome = txtInfoUnit.Substring(234, 40).Trim();
                        r.documento = txtInfoUnit.Substring(33, 14).TrimStart('0');
                        r.path = txtFiles[i];
                        boletosTxt.Add(r);
                    }
                }
                #endregion

                #region Aggregate PDF bills
                List<Boleto> toDelete = new List<Boleto>();
                List<Boleto> toAdd = new List<Boleto>();
                foreach (Boleto b in boletosPdf)
                {
                    if (toDelete.Contains(b))
                        continue;
                    if (boletosPdf.Where(bol => b.documento == bol.documento).Count() == 1)
                        continue;
                    Boleto novo = new Boleto();
                    novo.documento = b.documento;
                    novo.nome = b.nome;
                    novo.path = b.path;
                    novo.preco = boletosPdf.Where(bol => b.documento == bol.documento).Sum(bol => bol.preco);
                    toAdd.Add(novo);
                    toDelete.AddRange(boletosPdf.Where(bol => b.documento == bol.documento));
                }
                
                boletosPdf.RemoveAll(b => toDelete.Contains(b));
                boletosPdf.AddRange(toAdd);

                Boleto teste = boletosPdf.Where(bol => bol.documento == "100804717").FirstOrDefault();
                #endregion

                #region Check bills
                Errors.Add("Erro;Bill TXT;Bill PDF;Nome TXT;Nome PDF;Price TXT; Price PDF;File TXT;File PDF;Unidade");
                pathAssociations = new SortedList<string, string>();
                foreach (Boleto boletoPdf in boletosPdf)
                {
                    if(!pathAssociations.ContainsKey(Path.GetFileName(boletoPdf.path)))
                        if (boletosTxt.Where(b => b.documento == boletoPdf.documento && b.nome == boletoPdf.nome && b.preco == boletoPdf.preco).Count() == 1)
                            pathAssociations.Add(Path.GetFileName(boletoPdf.path), Path.GetFileName(boletosTxt.Where(b => b.documento == boletoPdf.documento).First().path));
                }

                foreach (String pdfFile in pdfFiles)
                {
                    if (pathAssociations.ContainsKey(Path.GetFileName(pdfFile)))
                        Console.WriteLine("\t" + Path.GetFileName(pdfFile) + " ....... " + pathAssociations[Path.GetFileName(pdfFile)]);
                    else
                        Console.WriteLine("X\t" + Path.GetFileName(pdfFile) + " ....... ?");
                }
                foreach (String txtFile in txtFiles)
                    if(!pathAssociations.ContainsValue(Path.GetFileName(txtFile)))
                        Console.WriteLine("X\t?" + " ....... " + Path.GetFileName(txtFile));

                foreach (Boleto boletoPdf in boletosPdf)
                {
                    if (boletosTxt.Where(b => b.documento == boletoPdf.documento).Count() == 0)
                    {
                        if (boletosTxt.Where(b => b.nome == boletoPdf.nome).Count() == 1)
                        {
                            Errors.Add(String.Format("Wrong Document code;{0};{1};{2};{2};{3};{4};{5};{6};{7}", boletosTxt.Where(b => b.nome == boletoPdf.nome).First().documento, boletoPdf.documento, boletoPdf.nome, boletosTxt.Where(b => b.nome == boletoPdf.nome).First().preco, boletoPdf.preco,Path.GetFileName(boletosTxt.Where(b => b.nome == boletoPdf.nome).First().path), Path.GetFileNameWithoutExtension(boletoPdf.path), unidade.nome));
                            //Errors.Add("\t" + String.Format("Wrong Document code in for bill #{0}|#{1} in name of {2} on file {3}|{4}.", boletosTxt.Where(b => b.nome == boletoPdf.nome).First().documento, boletoPdf.documento, boletoPdf.nome, Path.GetFileName(boletosTxt.Where(b => b.nome == boletoPdf.nome).First().path), Path.GetFileNameWithoutExtension(boletoPdf.path)));
                            if (boletosTxt.Where(b => b.nome == boletoPdf.nome).First().preco != boletoPdf.preco)
                                //Errors.Add("\t" + String.Format("Prices do not match ({5}|{6}) for bills #{0}|#{1} in name of {2} on file {3}|{4}.", boletosTxt.Where(b => b.nome == boletoPdf.nome).First().documento, boletoPdf.documento, boletoPdf.nome, Path.GetFileName(boletosTxt.Where(b => b.nome == boletoPdf.nome).First().path), Path.GetFileNameWithoutExtension(boletoPdf.path), boletosTxt.Where(b => b.nome == boletoPdf.nome).First().preco, boletoPdf.preco));
                                Errors.Add(String.Format("Prices do not match;{0};{1};{2};{2};{3};{4};{5};{6};{7}", boletosTxt.Where(b => b.nome == boletoPdf.nome).First().documento, boletoPdf.documento, boletoPdf.nome, boletosTxt.Where(b => b.nome == boletoPdf.nome).First().preco, boletoPdf.preco, Path.GetFileName(boletosTxt.Where(b => b.nome == boletoPdf.nome).First().path), Path.GetFileNameWithoutExtension(boletoPdf.path), unidade.nome));
                        }
                        else
                            //Errors.Add("\t" + String.Format("Could not find match for bill #{0} in name of {1} on file {2}.", boletoPdf.documento, boletoPdf.nome, Path.GetFileNameWithoutExtension(boletoPdf.path)));
                            Errors.Add(String.Format("Could not find txt match;;{0};;{1};;{2};;{3};{4}", boletoPdf.documento, boletoPdf.nome, boletoPdf.preco, Path.GetFileNameWithoutExtension(boletoPdf.path), unidade.nome));
                    }
                    else if (boletosTxt.Where(b => b.documento == boletoPdf.documento).Count() > 1)
                        //Errors.Add("\t" + String.Format("({0})Repeated bill #{1} on files {2}.", boletosTxt.Where(b => b.documento == boletoPdf.documento).Count(), boletoPdf.documento, String.Join(",", boletosTxt.Where(b => b.documento == boletoPdf.documento).Select(b => Path.GetFileName(b.path)))));
                        Errors.Add(String.Format("Repeated bill;;{0};;{1};;{2};{3}", boletoPdf.documento, boletoPdf.nome, String.Join(",", boletosTxt.Where(b => b.documento == boletoPdf.documento).Select(b => Path.GetFileName(b.path)), unidade.nome)));
                    else // Perfect association made by document code
                    {
                        Boleto boletoTxt = boletosTxt.Where(b => b.documento == boletoPdf.documento).First();
                        if (boletoTxt.nome != boletoPdf.nome)
                            //Errors.Add("Erro;Bill TXT;Bill PDF;Nome TXT;Nome PDF;Price TXT; Price PDF;File TXT;File PDF");
                            Errors.Add(String.Format("Names do not match;{0};{1};{2};{3};{4};{5};{6};{7};{8}", boletoTxt.documento, boletoPdf.documento, boletoTxt.nome, boletoPdf.nome, boletoTxt.preco, boletoPdf.preco, Path.GetFileName(boletoTxt.path), Path.GetFileName(boletoPdf.path), unidade.nome));
                        if (boletoTxt.preco != boletoPdf.preco)
                            //Errors.Add("\t" + String.Format("Prices do not match ({5},{6}) on bill #{0} in name of {1}, files {3} and {4}", boletoTxt.documento, boletoTxt.nome, boletoPdf.nome, Path.GetFileName(boletoTxt.path), Path.GetFileName(boletoPdf.path), boletoTxt.preco, boletoPdf.preco));
                            Errors.Add(String.Format("Prices do not match;{0};{1};{2};{3};{4};{5};{6};{7};{8}", boletoTxt.documento, boletoPdf.documento, boletoTxt.nome, boletoPdf.nome, boletoTxt.preco, boletoPdf.preco, Path.GetFileName(boletoTxt.path), Path.GetFileName(boletoPdf.path), unidade.nome));
                    }
                }

                //foreach (Boleto boletoTxt in boletosTxt)
                //{
                //    if (boletosPdf.Where(b => b.documento == boletoTxt.documento).Count() == 0)
                //        Errors.Add(String.Format("Could not find match for bill #{0} in name of {1} on file {2}.", boletoTxt.documento, boletoTxt.nome, Path.GetFileNameWithoutExtension(boletoTxt.path)));
                //    else if (boletosPdf.Where(b => b.documento == boletoTxt.documento).Count() > 1)
                //        Errors.Add(String.Format("Repeated bill #{0} on file {1}.", boletoTxt.documento, Path.GetFileNameWithoutExtension(boletoTxt.path)));
                //    else
                //    {
                //        Boleto boletoPdf = boletosTxt.Where(b => b.documento == boletoTxt.documento).First();
                //        if (boletoTxt.nome != boletoPdf.nome)
                //            Errors.Add(String.Format("Names do not match on bill #{0} in name of {1}(Txt) and {2}(Pdf), files {3} and {4}", boletoTxt.documento, boletoTxt.nome, boletoPdf.nome, boletoTxt.path, boletoPdf.path));
                //        if (boletoTxt.preco != boletoPdf.preco)
                //            Errors.Add(String.Format("Names do not match on bill #{0} in name of {1}, files {3} and {4}", boletoTxt.documento, boletoTxt.nome, boletoPdf.nome, boletoTxt.path, boletoPdf.path));
                //    }
                //}
                #endregion
            }

            using (StreamWriter writer = new StreamWriter("Errors.txt"))
            {
                foreach (String error in Errors)
                    writer.WriteLine(error);
            }

            Console.WriteLine();
            Console.Write("Press any key to exit...");
            Console.ReadKey();
        }
        public static String RemoveStress(String word)
        {
            word = word.Replace("Á", "A").Replace("À", "A").Replace("Ã", "A").Replace("Â", "A");
            word = word.Replace("É", "E").Replace("È", "E").Replace("Ê", "E");
            word = word.Replace("Í", "I").Replace("Ì", "I").Replace("Î", "I");
            word = word.Replace("Ó", "O").Replace("Ò", "O").Replace("Õ", "O").Replace("Ô", "O");
            word = word.Replace("Ú", "U").Replace("Ù", "U").Replace("Û", "U");
            word = word.Replace("Ç", "C");
            return word;
        }

        public static String[] ReadPdfFile(String fileName)
        {
            PdfReader reader2 = new PdfReader(fileName);
            string strText = string.Empty;

            for (int page = 1; page <= reader2.NumberOfPages; page++)
            {
                //ITextExtractionStrategy its = new iTextSharp.text.pdf.parser.SimpleTextExtractionStrategy();
                ITextExtractionStrategy its = new iTextSharp.text.pdf.parser.SimpleTextExtractionStrategy();
                PdfReader reader = new PdfReader(fileName);
                String s = PdfTextExtractor.GetTextFromPage(reader, page, its);

                s = Encoding.UTF8.GetString(ASCIIEncoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(s)));
                strText = strText + s;
                reader.Close();
            }
            return strText.Split(new string[] {"Cedente:"}, StringSplitOptions.RemoveEmptyEntries);
        }

        public static String[] ReadTxtFile(String fileName)
        {
            List<String> output = new List<string>();
            using (StreamReader reader = new StreamReader(fileName))
            {
                String line;
                reader.ReadLine(); // Cabeçalho
                while((line = reader.ReadLine()) != null)
                    output.Add(line);
            }
            output.RemoveAt(output.Count - 1);
            return output.ToArray();
        }    
    }
}
